package genedit.utility;

import genedit.gui.exceptions.ElementNePostoji;
import genedit.gui.propertyDialogs.DialogTableModel;
import genedit.model.Document;
import genedit.model.HierarchyElement;
import genedit.model.Package;
import genedit.model.Project;
import genedit.model.graph.ConnectionPoint;
import genedit.model.graph.GraphDocument;
import genedit.model.graph.GraphElement;
import genedit.model.graph.Link;
import genedit.model.graph.Property;
import genedit.model.graph.classModel.AggregationLink;
import genedit.model.graph.classModel.AssociationLink;
import genedit.model.graph.classModel.ClassModel;
import genedit.model.graph.classModel.CompositionLink;
import genedit.model.graph.classModel.GAttribute;
import genedit.model.graph.classModel.GAttributes;
import genedit.model.graph.classModel.GMethod;
import genedit.model.graph.classModel.GMethods;
import genedit.model.graph.classModel.GParameter;
import genedit.model.graph.classModel.GeneralizationLink;
import genedit.model.graph.classModel.InterfaceModel;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

public class CodeGenerator {
	protected static String startPath;
	
	public static void generateCode(Project project,File directory){
		startPath=directory.getAbsolutePath();
		for(Package pack:project.getPackages()){
			processPackage(pack, directory);
		}
	}
	
	protected static void processPackage(Package pack,File directory){
		File newDirectory=new File(directory.getAbsolutePath()+"\\"+pack.getName());
		newDirectory.mkdir();
		for(HierarchyElement element:pack.getElements()){
			if(element instanceof Package){
				processPackage((Package)element, newDirectory);
			}else if(element instanceof Document){
				processDocument((Document)element, newDirectory);
			}
		}
	}
	
	protected static void processDocument(Document document,File directory){
		//neka se subdokument ponasa kao paket
		for(Document subdoc:document.getSubDocument()){
			File newDirectory=new File(directory.getAbsolutePath()+"\\"+document.getName());
			newDirectory.mkdir();
			processDocument(subdoc, newDirectory);
		}
		
		File newPackage=new File(directory.getAbsolutePath()+"\\"+document.getName());
		newPackage.mkdir();
		processElements(document, newPackage);
	}
	
	protected static void processElements(Document document,File directory){		
		String currentPackage=directory.getAbsolutePath().replace(startPath+"\\", "").replace("\\", ".");
		
		GraphDocument graphDocument=(GraphDocument)document;
		for(GraphElement element:graphDocument.getElements()){
			if(element instanceof ClassModel){
				printClassModel((ClassModel)element,directory,currentPackage);
			}else if(element instanceof InterfaceModel){
				printInterfaceModel((InterfaceModel)element,directory,currentPackage);
			}
			/*
			else if(element instanceof AssociationLink){
				printAssociationLink((AssociationLink)element,directory,currentPackage);
			}else if(element instanceof AggregationLink){
				printAggregationLink((AggregationLink)element,directory,currentPackage);
			}else if(element instanceof CompositionLink){
				printCompositionLink((CompositionLink)element,directory,currentPackage);
			}else if(element instanceof GeneralizationLink){
				printGeneralizationLink((GeneralizationLink)element,directory,currentPackage);
			}
			*/
		}
	}
	
	protected static void printClassModel(ClassModel classModel,File pack,String packName){
		PrintWriter writer=null;
		try {
			writer=new PrintWriter(new File(pack.getAbsolutePath()+"\\"+classModel.getName()+".java"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		String[] packageEl = packName.split("\\.");
		writer.println("package " + packName.replace(" ", "")+";");
		writer.println();
		writer.print("public ");
		if(classModel.isStaticEl())
			writer.print("static ");
		if(classModel.isFinaltEl())
			writer.print("final ");
		if(classModel.isAbstractEl())
			writer.print("abstract ");
		writer.print("class " + classModel.getName().replace(" ", ""));
		for(ConnectionPoint cp : classModel.getConnectionPoints())
			for(Link link : cp.getStarting()) {
				 if(link instanceof GeneralizationLink && link.getEnd().getParent() instanceof ClassModel){
					 writer.print(" extends " + link.getEnd().getParent().getName().replace(" ", ""));
					 break;
				 }
			}
		boolean exp = true;
		for(ConnectionPoint cp : classModel.getConnectionPoints())
			for(Link link : cp.getStarting()) {
				 if(link instanceof GeneralizationLink && link.getEnd().getParent() instanceof InterfaceModel){
					 if(exp){
						 writer.print(" implements " + link.getEnd().getParent().getName().replace(" ", ""));
						 exp = false;
					 }else
						 writer.print(", " + link.getEnd().getParent().getName().replace(" ", ""));
				 }
			}
		writer.println("{");
		writer.println();
		for (Property p : classModel.getProperties()) {
			if(p.getKey().equals("attributes"))
			for(String name : ((GAttributes)p.getValue()).getAtributeKeys()){
				try {
					writer.print("\t"+((GAttributes)p.getValue()).getAtribute(name).getVisibility());
					writer.print(" "+((GAttributes)p.getValue()).getAtribute(name).getType());
					writer.print(" "+((GAttributes)p.getValue()).getAtribute(name).getName());
					if(!((GAttributes)p.getValue()).getAtribute(name).getPreDef().equals("")){
						writer.print(" = " + ((GAttributes)p.getValue()).getAtribute(name).getPreDef());
					}
					writer.println(";");						
				} catch (ElementNePostoji e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		for(ConnectionPoint cp : classModel.getConnectionPoints())
			for(Link link : cp.getStarting()) {
				 if(!(link instanceof GeneralizationLink)){ 
					 writer.println("\tprivate java.util.Collection" + "<"+link.getEnd().getParent().getName()+"> "+link.getEnd().getParent().getName().toLowerCase()+";");
				 }
			}
		writer.println();
		for (Property p : classModel.getProperties()) {
			if(p.getKey().equals("methods"))
			for(GMethod method : ((GMethods)p.getValue()).getMethods()){
					writer.print("\t" + method.getVisibility());
					writer.print(" " + method.getReturnType());
					writer.print(" " + method.getName() + "(");
					int i = 0;
					for(String param : method.getParameteres().getParameterKeys()){
						try {
							if(i != method.getParameteres().getParameters().size()-1)					
								writer.print(method.getParameteres().getParameter(param).toString() + ",");					
							else
								writer.print( method.getParameteres().getParameter(param).toString());
								} catch (ElementNePostoji e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
						i++;
						}
					writer.println("){");
					if(method.getReturnType().equals("byte") || method.getReturnType().equals("int")  || method.getReturnType().equals("short")  ||
						method.getReturnType().equals("long") || method.getReturnType().equals("float") || method.getReturnType().equals("double"))
								writer.println("\t\t" + "return 0;");
					else if(method.getReturnType().equals("char"))
							writer.println("\t\t" + "return \'\\u0000\';");
					else if(method.getReturnType().equals("boolean"))
							writer.println("\t\t" + "return false;");
					else
						writer.println("\t\t" + "return null;");
					writer.println("\t}");
					writer.println();					
			}
		}
		
		for (Property p : classModel.getProperties()) {
			if(p.getKey().equals("attributes"))
			for(String name : ((GAttributes)p.getValue()).getAtributeKeys()){
				try {
					if(((GAttributes)p.getValue()).getAtribute(name).isGetAttribute()){
						if(name.length()==1)
							writer.println("\t" + "public " + ((GAttributes)p.getValue()).getAtribute(name).getType() + " get" + Character.toUpperCase(name.charAt(0))
							+ "(" +  "){");
						else
							writer.println("\t" + "public " + ((GAttributes)p.getValue()).getAtribute(name).getType() + " get" + Character.toUpperCase(name.charAt(0))
							+ name.substring(1) + "(" +  "){");
						writer.println("\t\treturn " + name.toLowerCase() + ";");
						writer.println("\t}");
						writer.println();
					}
					if(((GAttributes)p.getValue()).getAtribute(name).isSetAttribute()){
						if(name.length()==1)
						writer.println("\t" + "public void " + " set" + Character.toUpperCase(name.charAt(0)) + "(" + ((GAttributes)p.getValue()).getAtribute(name).getType()
								+ " " + name.toLowerCase() + "){"); 
						else
						writer.println("\t" + "public void " + " set" + Character.toUpperCase(name.charAt(0)) + name.substring(1) +  "(" 
						+ ((GAttributes)p.getValue()).getAtribute(name).getType() + " " + name.toLowerCase() + "){"); 
						writer.println("\t\tthis." + name.toLowerCase() + " = " + name.toLowerCase() + ";");
						writer.println("\t}");
						writer.println();
					}						
				} catch (ElementNePostoji e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				   }
			}
		}
		
		writer.println("}");
		writer.close();
	}
	
	protected static void printInterfaceModel(InterfaceModel classModel,File pack,String packName){
		PrintWriter writer=null;
		try {
			writer=new PrintWriter(new File(pack.getAbsolutePath()+"\\"+classModel.getName()+".java"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		String[] packageEl = packName.split("\\.");
		writer.println("package " + packName.replace(" ", "")+";");
		writer.println();
		writer.print("public ");
		writer.print("interface " + classModel.getName().replace(" ", ""));
		boolean exp = true;
		for(ConnectionPoint cp : classModel.getConnectionPoints())
			for(Link link : cp.getStarting()) {
				 if(link instanceof GeneralizationLink && link.getEnd().getParent() instanceof InterfaceModel){
					 if(exp){
						 writer.print(" implements " + link.getEnd().getParent().getName().replace(" ", ""));
						 exp = false;
					 }else
						 writer.print(", " + link.getEnd().getParent().getName().replace(" ", ""));
				 }
			}
		writer.println("{");
		writer.println();
		for (Property p : classModel.getProperties()) {
			if(p.getKey().equals("attributes"))
			for(String name : ((GAttributes)p.getValue()).getAtributeKeys()){
				try {
					writer.print("\t"+"public static final");
					writer.print(" "+((GAttributes)p.getValue()).getAtribute(name).getType());
					writer.print(" "+((GAttributes)p.getValue()).getAtribute(name).getName());
					if(!((GAttributes)p.getValue()).getAtribute(name).getPreDef().equals("")){
						writer.print(" = " + ((GAttributes)p.getValue()).getAtribute(name).getPreDef());
					}
					writer.println(";");						
				} catch (ElementNePostoji e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		for(ConnectionPoint cp : classModel.getConnectionPoints())
			for(Link link : cp.getStarting()) {
				 if(!(link instanceof GeneralizationLink)){ 
					 writer.println("\tprivate java.util.Collection" + "<"+link.getEnd().getParent().getName()+"> "+link.getEnd().getParent().getName().toLowerCase()+";");
				 }
			}
		writer.println();
		for (Property p : classModel.getProperties()) {
			if(p.getKey().equals("methods"))
			for(GMethod method : ((GMethods)p.getValue()).getMethods()){
					writer.print("\t" + method.getVisibility());
					writer.print(" " + method.getReturnType());
					writer.print(" " + method.getName() + "(");
					int i = 0;
					for(String param : method.getParameteres().getParameterKeys()){
						try {
							if(i != method.getParameteres().getParameters().size()-1)					
								writer.print(method.getParameteres().getParameter(param).toString() + ",");					
							else
								writer.print( method.getParameteres().getParameter(param).toString());
								} catch (ElementNePostoji e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
						i++;
						}
					writer.println(");");
					writer.println();					
			}
		}
		
	
		writer.println("}");
		writer.close();
	}

}
